﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace Fabrica
{
    [Serializable, Description( "Oferece acesso e manipulação dos dados existentes na entidade." )]
    public class Repositorio<TContexto, T> : IDisposable, IRepositorio<T> where T : class
    {
        [Description( "Contexto do Serviço de Acesso a dados." )]
        public DbContext Contexto { get; set; }

        [Description( "String de conexão para acesso a dados." )]
        public String StringDeConexao { get; set; }

        public Repositorio( string stringDeConexao )
        {
            if ( string.IsNullOrEmpty( stringDeConexao ) )
                throw new ArgumentNullException( "stringDeConexao" );

            StringDeConexao = stringDeConexao;
            Contexto = ( DbContext )Activator.CreateInstance( typeof( TContexto ) );
        }

        public T ObtemPorID( Expression<Func<T, bool>> expressao )
        {
            try
            {
                return Contexto.Set<T>().SingleOrDefault( expressao );
            }
            catch ( Exception ex )
            {
                if ( ex.InnerException != null )
                    throw new Exception( ex.InnerException.Message, ex );

                throw new Exception( ex.Message, ex );
            }
        }

        public IList<T> ObtemTodos()
        {
            try
            {
                return Contexto.Set<T>().ToList();
            }
            catch ( Exception ex )
            {
                if ( ex.InnerException != null )
                    throw new Exception( ex.InnerException.Message, ex );

                throw new Exception( ex.Message, ex );
            }
        }

        public T Adicionar( T entidade )
        {
            try
            {
                Contexto.Set<T>().Add( entidade );
                Contexto.SaveChanges();
                return entidade;
            }
            catch ( Exception ex )
            {
                if ( ex.InnerException != null )
                    throw new Exception( ex.InnerException.Message, ex );

                throw new Exception( ex.Message, ex );
            }
        }

        public bool Excluir( T entidade )
        {
            try
            {
                Contexto.Set<T>().Remove( entidade );
                Contexto.SaveChanges();
                return true;
            }
            catch ( Exception ex )
            {
                if ( ex.InnerException != null )
                    throw new Exception( ex.InnerException.Message, ex );

                throw new Exception( ex.Message, ex );
            }
        }

        public bool Atualizar( T entidade )
        {
            try
            {
                if ( Contexto.Entry( entidade ).State == EntityState.Unchanged )
                    Contexto.Entry( entidade ).State = EntityState.Modified;

                Contexto.SaveChanges();
                return true;
            }
            catch ( Exception ex )
            {
                if ( ex.InnerException != null )
                    throw new Exception( ex.InnerException.Message, ex );

                throw new Exception( ex.Message, ex );
            }
        }

        public void Dispose()
        {
            try
            {
                Contexto.Dispose();
                GC.SuppressFinalize( this );
            }
            catch ( Exception ex )
            {
                throw ex;
            }
        }
    }
}
